#include <string.h>

#include "lib.h"

#include "bwldguts.h"
#include "cardmgr.h"
#include "shue.h"
#include "vphand.h"

#include "pte_bwld.h"	// PTE_xxx -> PayTableEvents...i.e. VPEVT_xxx

static CardMgr stdMgr;

Shue*
BonusWildGuts::newShue(const ShueGameInfo &sgi)
{
	return new Shue(&stdMgr, 1, sgi);
}

CardMgr*
BonusWildGuts::getCardMgr()
{
	return &stdMgr;
}

int32
BonusWildGuts::CalcPayout(int32* pEvent, VPHand* pHand, int32 Bet,
	CalcPayoutMode, uint8* subGameData, int32 length)
{
	*pEvent = classifyHand(pHand);

	return *pEvent != VPEVT_LOSER ? PTBLENTRY(Bet-1, *pEvent) : 0;
}

int32
BonusWildGuts::classifyHand(VPHand* pHand)
{
	uint8 counts[13];
	int32 i;
	uint8 *probe, *first, *nonWild[3];
	bool8 flush = true, haveFlushSuit = false, haveAce;
	uint8 flushSuit;

#define return_5ofaKind_category(rank)		\
	switch (rank)							\
	{										\
		case CardMgr::ACE:					\
			return VPEVT_5ACES;				\
		case 3:								\
		case 4:								\
		case 5:								\
			return VPEVT_5OFAKIND_3_4_5;	\
		default:							\
			return VPEVT_5OFAKIND_6_THRU_K;	\
	}

	if (!pHand || (pHand && pHand->getCount() != 5))
		return VPEVT_LOSER;

	memset(counts, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12

	flushSuit = stdMgr.getSuit(pHand->getCardAt(0));

	for (i = 0 ; i < pHand->getCount(); i++)
	{
		++counts[stdMgr.getRank(pHand->getCardAt(i)) - 2];

		if (wildCard == stdMgr.getRank(pHand->getCardAt(i)))
			continue;	// wildcards don't count into flush status

		if (!haveFlushSuit)
		{
			flushSuit = stdMgr.getSuit(pHand->getCardAt(i));
			haveFlushSuit = true;
		}

		if (i > 0 && flush)
			flush = (bool8)(stdMgr.getSuit(pHand->getCardAt(i)) == flushSuit);
	}

	switch (counts[wildCard - 2])
	{
		case 4:
			probe = (uint8*)memchr(counts, 1, 13);
			if (probe - counts + 2 == CardMgr::ACE)
				return VPEVT_4WILD_WITH_ACE;
			else
				return VPEVT_4WILD;

		case 3:
			counts[wildCard-2] = 0;	// counts: |one 2|two 1|

			probe = (uint8*)memchr(counts, 2, 13);
			if (probe)
				return_5ofaKind_category(probe - counts + 2);

			// straights
			//
			if (flush)
			{
				haveAce = (bool8)(counts[CardMgr::ACE-2] != 0);
				first = (uint8*)memchr(counts, 1, 13);
				probe = (uint8*)memchr(first + 1, 1, 13 - (first-counts+1));
				if (probe - first <= 4)
				{
					if (first >= counts + 10-2)
						return VPEVT_ROYALFLUSHWILD;
					else
						return VPEVT_STRAIGHTFLUSH;
				}

				if (haveAce && first <= counts+5-2)		// ace-low straight
					return VPEVT_STRAIGHTFLUSH;
			}

			// Can't be full house, flush, non-flush straight, or 3 kind
			// (4 of a kind pays more than all of those).
			//

			return VPEVT_4OFAKIND;

		case 2:
			counts[wildCard-2] = 0;	// counts: |one 3|one 2 & one 1|three 1|

			probe = (uint8*)memchr(counts, 3, 13);
			if (probe)
				return_5ofaKind_category(probe - counts + 2);

			if (memchr(counts, 2, 13))
				return VPEVT_4OFAKIND;

			// straights: insert wilds in sequence after/before
			// first non-wild card, check for straights, zap inserted wilds
			//
			haveAce = (bool8)(counts[CardMgr::ACE-2] != 0);
			first = nonWild[0] = (uint8*)memchr(counts, 1, 13);
			nonWild[1] = (uint8*)memchr(nonWild[0]+1, 1,
				13 - (nonWild[0]+1 - counts));
			i = 0;
			probe = first + 1;
			while (i < 2 && probe < counts + 13)
			{
				if (*probe == 0)
				{
					*probe = 1;
					i++;
				}
				++probe;
			}
			probe = first - 1;
			while (i < 2 && probe >= counts)
			{
				if (*probe == 0)
				{
					*probe = 1;
					i++;
				}
				--probe;
			}

			first = probe = (uint8*)memchr(counts, 1, 13);
			i = 0;
			while (probe < counts + 13)
				if (*probe++ == 1)
					i++;
				else
					break;

			if (i == 5)
			{
				if (flush)
					if (first == counts + 10-2)
						return VPEVT_ROYALFLUSHWILD;
					else
						return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			if (haveAce &&					// ace-low straights
				nonWild[0] <= counts+5-2 &&
				nonWild[1] <= counts+5-2)
			{
				if (flush)
					return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			// Can't be full house
			//

			return flush ? VPEVT_FLUSH : VPEVT_3OFAKIND;

		case 1:
			counts[wildCard-2] = 0;

			// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
			//
			probe = (uint8*)memchr(counts, 4, 13);
			if (probe)
				return_5ofaKind_category(probe - counts + 2);

			if (memchr(counts, 3, 13))	// 4 of a kind pays > full house
				return VPEVT_4OFAKIND;

			probe = (uint8*)memchr(counts, 2, 13);
			if (probe)					// at least one pair
			{
				if (probe < counts+(13-1) &&	// 2 pair -> full house
					memchr(probe+1, 2, 13 - (probe-counts+1)))
					return VPEVT_FULLHOUSE;
				else					// one pair: 3 of a kind pays > 2 pair
					return VPEVT_3OFAKIND;
			}

			// straights: insert wild in sequence after first non-wild card
			// check for straights, zap inserted wild
			//
			haveAce = (bool8)(counts[CardMgr::ACE-2] != 0);
			first = nonWild[0] = (uint8*)memchr(counts, 1, 13);
			nonWild[1] = (uint8*)memchr(nonWild[0]+1, 1,
				13 - (nonWild[0]+1 - counts));
			nonWild[2] = (uint8*)memchr(nonWild[1]+1, 1,
				13 - (nonWild[1]+1 - counts));
			i = 0;
			probe = first + 1;
			while (i < 1 && probe < counts + 13)
			{
				if (*probe == 0)
				{
					*probe = 1;
					i++;
				}
				++probe;
			}
			probe = first - 1;
			while (i < 1 && probe >= counts)
			{
				if (*probe == 0)
				{
					*probe = 1;
					i++;
				}
				--probe;
			}

			first = probe = (uint8*)memchr(counts, 1, 13);
			i = 0;
			while (probe < counts + 13)
				if (*probe++ == 1)
					i++;
				else
					break;

			if (i == 5)
			{
				if (flush)
					if (first == counts + 10-2)
						return VPEVT_ROYALFLUSHWILD;
					else
						return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			if (haveAce &&					// ace-low straights
				nonWild[0] <= counts+5-2 &&
				nonWild[1] <= counts+5-2 &&
				nonWild[2] <= counts+5-2)
			{
				if (flush)
					return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			if (flush)
				return VPEVT_FLUSH;

			return VPEVT_LOSER;

		default:
			// 3-of-a-kind, 4-of-a-kind, full house (no pairs)
			//
			if (memchr(counts, 4, 13))
				return VPEVT_4OFAKIND;

			if (memchr(counts, 3, 13))
				if (memchr(counts, 2, 13))
					return VPEVT_FULLHOUSE;
				else
					return VPEVT_3OFAKIND;

			// straights
			//
			haveAce = (bool8)(counts[CardMgr::ACE-2] != 0);
			first = probe = (uint8*)memchr(counts, 1, 13);
			i = 0;
			while (probe < counts + 13)
				if (*probe++ == 1)
					i++;
				else
					break;

			if (i == 5)
			{
				if (flush)
					if (first == counts + 10-2)
						return VPEVT_ROYALFLUSH;
					else
						return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			if (i == 4 && haveAce && first == counts) // ace-low straights
			{
				// not possible with wildcard <= 5 or wildcard == ACE
				//
				if (flush)
					return VPEVT_STRAIGHTFLUSH;

				return VPEVT_STRAIGHT;
			}

			return flush ? VPEVT_FLUSH : VPEVT_LOSER;
	}
}

int32
BonusWildGuts::doubleUpCompare(uint8 dealer, uint8 player)
{
	int32 dRank, pRank;

	//
	// NOTE WELL:  This function only works on a standard 52-card deck of
	//			   which 4 cards are wild (wildCard).
	//

	dRank = stdMgr.getRank(dealer);
	pRank = stdMgr.getRank(player);

	if (wildCard == dRank && wildCard == pRank)
		return 0;
	else if (wildCard == dRank)
		return 1;
	else if (wildCard == pRank)
		return -1;
	else
		return VPGuts::doubleUpCompare(dealer, player);
}
